package collector.httpd;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import net.apexcns.core.util.ConfigUtil;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerMetaData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import collector.core.CollectConstants;
import collector.core.ConfigTask;
import collector.core.TaskConstVO;
import collector.core.config.Config;
import collector.core.config.ConfigMetaExec;
import collector.core.util.JobTaskUtil;
import collector.core.util.ScheduleUtil;
import collector.httpd.MetaExecListener.EventType;
import collector.httpd.metad.MetaHandler;
import collector.metaexec.MetaExec;
import collector.task.JobTaskVO;


public class MetaServer {
	private static Logger _log = LoggerFactory.getLogger(MetaServer.class);
	private static final String _LogPrefixStr = CollectConstants.LOGGER_MODULE_NAME_CMS + CollectConstants.LOGGER_MODULE_NAME_AFTFIX;
	
	private Config config = Config.newInstance();
	//private ConfigModule configModule = ConfigModule.newInstance();
	private Server server;
	
	private Scheduler sched;
	private ConfigTask configTask = new ConfigTask();
	
	private List<MetaExec> execList = new ArrayList<MetaExec>();
	private transient List<MetaExecListener> execListeners = new CopyOnWriteArrayList<MetaExecListener>();

	List<JobTaskVO> jobTaskList = new ArrayList<JobTaskVO>();
	Map<String, JobTaskVO> jobTaskMap = new HashMap<String, JobTaskVO>();
	
	public MetaServer() {
	}
	
	private void init() {
		// STEP1 : 핸들러, 콜렉터 초기화
		initHandlers();
		initCollector();
		
//		StdErrLog l = new StdErrLog();
//		l.setDebugEnabled(false);
//		System.setErr(new PrintStream(new JscLoggingOutputStream(org.apache.log4j.Logger.getRootLogger(), Level.ERROR), true));
//		System.setOut(new PrintStream(new JscLoggingOutputStream(org.apache.log4j.Logger.getRootLogger(), Level.INFO), true));

		// STEP2 : 서버 생성 및 콘넥션 포트 설정
		server = new Server();
		Connector connector = new SocketConnector();
		//connector.setHost("::");
		connector.setPort(config.getMetaPort());
		server.setConnectors(new Connector[] { connector });
		
		// STEP3 : 콜렉션 핸들러 생성 및 설정
		HandlerCollection handlers = new HandlerCollection();
		handlers.setHandlers(new Handler[] { new MetaHandler(execList) });
		server.setHandler(handlers);

		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				try {
					MetaServer.this.stop();
				} catch (Exception e) {
				    _log.warn(_LogPrefixStr + "Stop Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
				}
			}
		});
		_log.info(_LogPrefixStr + "metaServer init. " + "[state="+server.getState() + "]");
	}
	
	/** <pre>
	 * Method : MetaServer.initCollector()
	 * 
	 * <br>
	 * 	return type of void
	 * <br> 
	 * </pre>
	 */
	private void initCollector() {
		_log.info(_LogPrefixStr + "----- Initializing (load Config, generate sched) -------------------");
	    	configTask.loadConfigTask(); 
	    	_log.debug(_LogPrefixStr + " Config loaded :: " + configTask);

		try {
		    sched = ScheduleUtil.sf.getScheduler();
		} catch (SchedulerException e) {
		    _log.error(_LogPrefixStr + "Scheduler init Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
		}
		registCollector();
		_log.info(_LogPrefixStr + "----- Initialization Complete (load Config, generate sched) --------");
	}

	private void registCollector() {
	        _log.info(_LogPrefixStr + "----- Scheduling Jobs to sched ----------------");
	        int idx = 1;
	        JobTaskVO jobTaskVo = null;
	        TaskConstVO tConvVo = null;
	        String keyTaskName = null;
	        
	        Map<String, TaskConstVO> conVoMap = configTask.getConVoMap();
	        
	        // Case for Map Type
	        @SuppressWarnings("rawtypes")
	        Iterator itr = conVoMap.keySet().iterator();
	        while (itr.hasNext()) {
	            keyTaskName = (String)itr.next();
	            tConvVo = conVoMap.get(keyTaskName);
//	            try {
//	            	String schedId = sched.getSchedulerInstanceId();
//	            } catch (SchedulerException e1) {
//	            	// TODO Auto-generated catch block
//	            	e1.printStackTrace();
//	            }
	            try {
					jobTaskVo = JobTaskUtil.generateJobTask(keyTaskName, tConvVo.getTaskGroup(), tConvVo.getTaskClassStr(), 
    						keyTaskName + CollectConstants.DEFAULT_TRIGGER_NAME_AFTFIX, 
    						keyTaskName + CollectConstants.DEFAULT_TRIGGER_GROUPNAME_AFTFIX, 
    						tConvVo.getScheduleDateStr(), tConvVo.getDurationType(), 
    						tConvVo.getDurationValue());
					
	            	sched = ScheduleUtil.scheduleJobTask(sched, jobTaskVo);
			    } catch (Exception e) {
			    	_log.warn(_LogPrefixStr + "Job Task Schedule Regist Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
			    }

	            // 스케줄 관련 작업 정보 출력
	            String taskConstMsg = tConvVo.viewJobSchedule();	// 설정 정보를 통해 로드된 작업 정보 
	            String  jobTaskScheduleInfo = jobTaskVo.toStringInfoSchedule(); // 스케줄러에 등록된 작업 정보 
	            _log.debug(_LogPrefixStr + "Scheduled Job Info IDX[" + idx + "] ---------------- ---------------- ---------------- ----------------");
	            _log.debug(_LogPrefixStr + "\t"+ "loaded tConvVo | " + taskConstMsg);
	            _log.debug(_LogPrefixStr + "\t"+ "Registed jobTaskVo | " + jobTaskScheduleInfo);
	            
	            jobTaskList.add(jobTaskVo);
	            jobTaskMap.put(jobTaskVo.getJobIDs(), jobTaskVo);
	            idx++;
	        }
	        
	        // FIXME::List JobListener Regist
//	        List<Matcher<Jobkey>> matcherJobList = new ArrayList();
//	        try {
//	            sched = ScheduleUtil.addJobListenerForJobTasks(sched, new ArrayList());
//	            
//	        } catch (Exception e) {
//	            _log.warn(_LogPrefixStr + "Job Listener Regist Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
//	        }
	        
	        // Case for List Type
//	        for ( TaskConstVO tConvVo : configTask.getConVoMap() ) {
//	            
//		        jobTaskVo = JobTaskUtil.generateJobTask(tConvVo.getTaskName(), tConvVo.getTaskGroup(),  (Class<? extends Job>) tConvVo.getTaskClass(), tConvVo.getTaskName()+"_Trigger", tConvVo.getTaskName()+"_TriggerGroup", tConvVo.getScheduleDateStr(), tConvVo.getDurationType(), tConvVo.getDurationValue());
//		        sched = ScheduleUtil.scheduleJobTask(sched, jobTaskVo);
//		        jobTaskList.add(jobTaskVo);
//	        }
	        _log.info(_LogPrefixStr + "----- Scheduled Jobs [" + jobTaskList.size() + "] records" + " ----------------");
	        _log.debug(_LogPrefixStr + "Registed jobTaskList :: "+jobTaskList);
	        	    
	}

	private void startCollector() {
        _log.info(_LogPrefixStr + "----- Starting Scheduler ----------------");
        // All of the jobs have been added to the scheduler, but none of the jobs
        // will run until the scheduler has been started
        try {
		    sched.start();
		} catch (SchedulerException e) {
		    _log.warn(_LogPrefixStr + "Start Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
		}
        _log.info(_LogPrefixStr + "----- Started Scheduler -----------------");
	}
	
	private void shutdownCollector() {
        _log.info(_LogPrefixStr + "----- Shutting Down Scheduler ---------------------");
        try {
		    sched.shutdown(true);
		} catch (SchedulerException e) {
		    _log.warn(_LogPrefixStr + "Shutdown Scheduler Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
		}

        SchedulerMetaData metaData;
		try {
		    metaData = sched.getMetaData();
		    _log.info(_LogPrefixStr + "Executed Total [" + metaData.getNumberOfJobsExecuted() + "] jobs.");
		} catch (SchedulerException e) {
		    _log.warn(_LogPrefixStr + "Shutdown Scheduler Meta Info Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
		}
        _log.info(_LogPrefixStr + "----- Shutdown Scheduler Complete -----------------");
	}
	
	public void start() throws Exception {
		if(server == null) init();
		server.start();
		_log.info(_LogPrefixStr + "metaServer started. " + "[state="+server.getState()+", port=" + config.getMetaPort() + "]");
		
		// Do task
		startCollector();
	}
	
	public void join() throws InterruptedException {
		server.join();
		_log.info(_LogPrefixStr + "metaServer join. " + "[state="+server.getState() + "]");
	}
	
	public void stop() throws Exception {
		server.stop();
		destroyHandlers();
		shutdownCollector();
		_log.info(_LogPrefixStr + "metaServer stopped. " + "[state="+server.getState()+", port=" + config.getMetaPort() + "]");
	}
	
	private void initHandlers() {
		List<ConfigMetaExec> list = config.getMetaExec();
		if(list==null) return;
		//execList = new ArrayList<MetaExec>();
		for(ConfigMetaExec exec : list) {
			if(!exec.isUseYn()) continue;
			try {
				@SuppressWarnings("rawtypes")
				Class clazz = Class.forName(exec.getClassName());
				//if(!clazz.isAssignableFrom(MetaDataHandler.class))
				//	throw new IllegalArgumentException("not a handler: " + exec.getClassName());
				MetaExec handler = (MetaExec) clazz.newInstance();
				execList.add(handler);
				if(handler instanceof MetaExecListener) {
					addMetaExecListener((MetaExecListener) handler);
				}
				_log.info(_LogPrefixStr + "metaExec: " + handler.getClass().getName());
			} catch (Exception e) {
			    _log.warn(_LogPrefixStr + "handle & metaExec Listener add Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
			}
		}
		
		if(execList == null) return;
		for(MetaExec handler : execList) {
			try {
				handler.initialize();
			} catch(Exception e) {
			    _log.error(_LogPrefixStr + "handle initialize Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
			}
		}
	}

	public synchronized void addMetaExecListener(MetaExecListener mel) {
		if (mel == null) return;
		execListeners.add(mel);
	}

	public synchronized void removeMetaExecListener(MetaExecListener mel) {
		if(mel == null) return;
		if(execListeners == null ) return;
		execListeners.remove(mel);
	}

	public void fireMetaEvent(EventType eventType, Object eventData) {
		if(execListeners==null) return;
		for(MetaExecListener mel : execListeners) {
			mel.handleMetaEvent(eventType, eventData);
		}
	}

	private void destroyHandlers() {
		if(execList == null) return;
		for(MetaExec handler : execList) {
			try {
				handler.destroy();
			} catch(Exception e) {
			    _log.error(_LogPrefixStr + "handle destroy Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
			}
		}
	}

	public static void main(String[] args) {
		System.setProperty(CollectConstants.DEFAULT_SERVER_MODE_KEYNAME, CollectConstants.DEFAULT_SERVER_MODE_KEYVALUE); 
		ConfigUtil.initLogger();

//		StdErrLog l = new StdErrLog();
//		l.setDebugEnabled(false);

//		String log4jfile = "conf/log4j.xml";
//		DOMConfigurator.configureAndWatch(log4jfile);

//		System.setErr(new PrintStream(new JscLoggingOutputStream(Logger.getRootLogger(), Level.ERROR), true));
//		System.setOut(new PrintStream(new JscLoggingOutputStream(Logger.getRootLogger(), Level.INFO), true));

		MetaServer metaServer = new MetaServer();

		try {
			metaServer.start();
			metaServer.join();
		} catch (Exception e) {
			_log.error(_LogPrefixStr + "metaserver Start Error - "+ e.getClass().getName() + ": " + e.getMessage(), e);
		}
	}
}
